/******************************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Ltd.                       */
/*                                                                            */
/*  NAME      = mysql_request.c                                               */
/*  NOTE      = MYSQL 解析请求包                                              */
/*  DATE      = 2015/04/20 by guojq                                           */
/******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <stdint.h>
#include <limits.h>

#include "mysql_core.h"
#include "mysql_request.h"

/*******************************************************************************
*  FUNC     :  response信息释放
*  ARGS     :  pp_st_request(request信息)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
void mysql_destroy_response(L7_resp_mysql **pp_st_response)
{
    if(pp_st_response == NULL || *pp_st_response == NULL)
        return;

    L7_resp_mysql *p_st_response = *pp_st_response;
    if (p_st_response->p_st_statement)
    {
        /* 释放SQL statement信息 */
        frdb_statement_free(&(p_st_response->p_st_statement));
    }
    if (p_st_response->p_error_msg)
    {
        frfree(p_st_response->p_error_msg);
    }

    frfree(p_st_response);
    *pp_st_response = NULL;
    return;
}

/*******************************************************************************
*  FUNC     :  上报响应结果信息
*  ARGS     :  p_st_conn(入力链接信息)
            :  p_st_private(MSSQL私有信息)
            :  p_st_hash_report((Frhash_header *))
*  RTN      :  APM_L7_ANALYZE_SUCC  (success)
*              APM_L7_ANALYZE_FAIL  (failed)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __mysql_report_data(Conntrack *p_st_conn
                           , L7session_mysql_private *p_st_private
                           , Frhash_header *p_st_hash_report
                           , struct timeval *p_st_timestamp)
{
    int i_ret = FR_SUCC;
    //Frtrans_info *p_st_trans = NULL;
    L7_req_mysql *p_st_req = p_st_private->p_st_req;
    L7_resp_mysql *p_st_resp = p_st_private->p_st_resp;
    L7_fragment_mysql *p_st_fragment = p_st_private->p_st_fragment;
    Sql_resultset *p_st_resultset = p_st_req->p_st_resultset;
    Sql_statement *p_st_statement = p_st_req->p_st_statement;
    Frreport_body_data *p_st_report_body = NULL;

    p_st_fragment->st_end_timestamp.tv_sec = p_st_timestamp->tv_sec;
    p_st_fragment->st_end_timestamp.tv_usec = p_st_timestamp->tv_usec;

    /* 更新response结果 */
    i_ret = frdb_set_resultset_resp2(p_st_resultset
                                    , p_st_fragment->u64_total_len
                                    , p_st_resp->ui_response_count
                                    , p_st_resp->arr_effect_rows
                                    , &(p_st_fragment->st_first_timestamp)
                                    , &(p_st_fragment->st_end_timestamp));
    if (i_ret != FR_SUCC)
    {
        frdbg("frdb_set_resultset_resp2 failed!\n");
        /* 销毁request和response数据 */
        mysql_destroy_request(&p_st_private->p_st_req);
        mysql_destroy_response(&p_st_private->p_st_resp);
        return APM_L7_ANALYZE_FAIL;
    }

    /* 格式化错误结果集 */
    p_st_report_body = mysql_new_report_body(p_st_conn, p_st_private->p_st_sess);
    /* 创建report body失败时，不释放做成的request */
    if (p_st_report_body == NULL)
    {
        frdbg("mysql_new_report_body failed!\n");
        /* 销毁request和response数据 */
        mysql_destroy_request(&p_st_private->p_st_req);
        mysql_destroy_response(&p_st_private->p_st_resp);
        return APM_L7_ANALYZE_SUCC;
    }
    
    //上报response信息
    i_ret = frdb_report_resp2(p_st_conn, p_st_hash_report, p_st_statement
                             , p_st_resultset
                             , p_st_resp->ll_error_num
                             , p_st_resp->p_error_msg
                             , p_st_resp->p_error_msg
                             , &p_st_report_body);
    /* 销毁response数据 */
    mysql_destroy_request(&p_st_private->p_st_req);
    mysql_destroy_response(&p_st_private->p_st_resp);
    /* 上报request数据失败时，不释放做成的request */
    if (i_ret != FR_SUCC)
    {
        frdbg("frdb_report_resp2 failed!\n");
        frreport_body_free(&p_st_report_body);
        return APM_L7_ANALYZE_SUCC;
    }

    return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);
}

FR_STATIC int __dissect_err_result(L7session_mysql_private *p_st_private)
{
    Frbyte_Packet *p_st_data = p_st_private->p_st_fragment->p_st_data;
    L7session_mysql *p_st_sess = p_st_private->p_st_sess;
    L7_resp_mysql *p_st_resp = p_st_private->p_st_resp;
    int i_errmsg = 0;
    int i_srv_status = 0;
    char c_status;
    char *p_errmsg = NULL;
    
    /*
        1	Error报文，值恒为0xFF
        老版本 4.0以前
        2	错误编号（小字节序）
        n	服务器消息
        新版本 4.1以后
        2	错误编号（小字节序）
        1	服务器状态标志，恒为'#'字符
        5	服务器状态（5个字符）
        n	服务器消息
    */
    if (bytebuff_get_int(p_st_data, 2, (uint32_t *)&i_srv_status, BYTE_LITTLE_ENDIAN) != FR_SUCC)
        return FR_FAIL;
    if (p_st_sess->i_version == MYSQL_VERSION_NEW)
    {
        if (bytebuff_peek_char(p_st_data, (uint8_t *)&c_status) != FR_SUCC)
        {
            FRLOG_WARN_DETAIL("mysql response peek failed, errormsg=%d", i_srv_status);
            return FR_FAIL;
        }
//        if (c_status != '#')
//        {
//            FRLOG_WARN_DETAIL("mysql response return2=====");
//            return FR_FAIL;
//        }
        //跳过1byte错误编号+5byte服务器状态
        if (bytebuff_skip(p_st_data, 6) != FR_SUCC)
        {
            FRLOG_WARN_DETAIL("mysql response skip failed, errormsg=%d", i_srv_status);
            return FR_FAIL;
        }
    }
    i_errmsg = bytebuff_remaining(p_st_data);
    p_errmsg = (char *)frmalloc(i_errmsg + 1);
    if (p_errmsg == NULL)
    {
        FRLOG_WARN_DETAIL("mysql response malloc failed, errormsg=%d", i_srv_status);
        return FR_FAIL;
    }

    memset(p_errmsg, 0x00, i_errmsg + 1);

    if (bytebuff_get_bytes(p_st_data, p_errmsg, i_errmsg) != FR_SUCC)
    {
        frfree(p_errmsg);
        FRLOG_WARN_DETAIL("mysql response getbytes failed, errormsg=%d", i_srv_status);
        return FR_FAIL;
    }
    
    p_st_resp->ll_error_num = i_srv_status;
    p_st_resp->p_error_msg = p_errmsg;
    
    return FR_SUCC;
    
}

FR_STATIC int __get_length_code(Frbyte_Packet *p_st_data, long long *p_ll_num)
{
    int i_len = 0;
    
    /*
    第一个字节值	后续字节数	长度值说明
    0-250	        0	        第一个字节值即为数据的真实长度
    251	            0	        空数据，数据的真实长度为零
    252	            2	        后续额外2个字节标识了数据的真实长度
    253	            3	        后续额外3个字节标识了数据的真实长度
    254	            8	        后续额外8个字节标识了数据的真实长度
    */
    if (bytebuff_get_int(p_st_data, 1, (uint32_t *)&i_len, BYTE_LITTLE_ENDIAN) == FR_FAIL)
        return FR_FAIL;
    
    switch (i_len)
    {
        case 251:
            *p_ll_num = 0;
            break;
        case 252:
            if (bytebuff_get_long(p_st_data, 2, (uint64_t *)p_ll_num, BYTE_LITTLE_ENDIAN)== FR_FAIL)
                return FR_FAIL;
            break;
        case 253:
            if (bytebuff_get_long(p_st_data, 3, (uint64_t *)p_ll_num, BYTE_LITTLE_ENDIAN)== FR_FAIL)
                return FR_FAIL;
            break;
        case 254:
            if (bytebuff_get_long(p_st_data, 8, (uint64_t *)p_ll_num, BYTE_LITTLE_ENDIAN)== FR_FAIL)
                return FR_FAIL;
            break;
        default:
            *p_ll_num = i_len;
    }
    return FR_SUCC;
}

FR_STATIC int __dissect_ok_result(L7session_mysql_private *p_st_private, int i_total_len, long long *p_ll_effect_num)
{
    long long ll_index_num = 0LL, ll_line_num = 0LL;
    int i_srv_status = 0, i_warn_num = 0;
    Frbyte_Packet *p_st_data = NULL;
    
    if (p_st_private == NULL || p_st_private->p_st_fragment == NULL || p_st_private->p_st_sess == NULL)
        return FR_FAIL;
    
    p_st_data = p_st_private->p_st_fragment->p_st_data;
    /*
        老版本 4.0以前
        1-9	受影响行数（Length Coded Binary）
        1-9	索引ID值（Length Coded Binary）
        2	服务器状态
        n	服务器消息（字符串到达消息尾部时结束，无结束符）
        新版本 4.1以后
        1-9	受影响行数（Length Coded Binary）
        1-9	索引ID值（Length Coded Binary）
        2	服务器状态
        2	告警计数
        n	服务器消息（字符串到达消息尾部时结束，无结束符）
    */
    
    if (p_st_private->p_st_sess->i_version == MYSQL_VERSION_OLD)
    {
        if (bytebuff_remaining(p_st_data) < 4)
            return FR_FAIL;
    }
    else
    {
        if (bytebuff_remaining(p_st_data) < 6)
            return FR_FAIL;
    }
    
    if (__get_length_code(p_st_data, &ll_line_num)== FR_FAIL)
        return FR_FAIL;
    
    if (__get_length_code(p_st_data, &ll_index_num)== FR_FAIL)
        return FR_FAIL;
    
    if (bytebuff_get_int(p_st_data, 2, (uint32_t *)&i_srv_status, BYTE_LITTLE_ENDIAN) == FR_FAIL)
        return FR_FAIL;
    
    if (p_st_private->p_st_sess->i_version == MYSQL_VERSION_NEW)
    {
        if (bytebuff_get_int(p_st_data, 2, (uint32_t *)&i_warn_num, BYTE_LITTLE_ENDIAN) == FR_FAIL)
            return FR_FAIL;
    }
    
    if (bytebuff_skip(p_st_data, bytebuff_remaining(p_st_data)) != FR_SUCC)
        return FR_FAIL;
    
    *p_ll_effect_num = ll_line_num;
    
    return FR_SUCC;
}

FR_STATIC int __dissect_result_header(L7session_mysql_private *p_st_private)
{
    Frbyte_Packet *p_st_data = p_st_private->p_st_fragment->p_st_data;
    L7_resp_mysql *p_st_resp = p_st_private->p_st_resp;
    long long ll_field_num = 0LL;
    
    /*
        1-9	Field结构计数（Length Coded Binary）
        1-9	额外信息（Length Coded Binary）
    */
    if (__get_length_code(p_st_data, &ll_field_num) == FR_FAIL)
        return FR_FAIL;
    
    if (bytebuff_skip(p_st_data, bytebuff_remaining(p_st_data))== FR_FAIL)
        return FR_FAIL;
    
    p_st_resp->ll_field_count = ll_field_num + 1;
    p_st_resp->i_status = MYSQL_RESPONSE_RESULT_FIELD;
    
    return FR_SUCC;
}

FR_STATIC int __dissect_result_field(L7session_mysql_private *p_st_private)
{
    L7_fragment_mysql *p_st_fragment = p_st_private->p_st_fragment;
    Frbyte_Packet *p_st_data = p_st_fragment->p_st_data;
    L7_resp_mysql *p_st_resp = p_st_private->p_st_resp;
    
    /*
    暂不需要解析其内容
    Field 结构:
        4.0 及之前的版本:
            n	数据表名称（Length Coded String）
            n	列（字段）名称（Length Coded String）
            4	列（字段）长度（Length Coded String）
            2	列（字段）类型（Length Coded String）
            2	列（字段）标志（Length Coded String）
            1	整型值精度
            n	默认值（Length Coded String）
        4.1 及之后的版本
            n	目录名称（Length Coded String）
            n	数据库名称（Length Coded String）
            n	数据表名称（Length Coded String）
            n	数据表原始名称（Length Coded String）
            n	列（字段）名称（Length Coded String）
            4	列（字段）原始名称（Length Coded String）
            1	填充值
            2	字符编码
            4	列（字段）长度
            1	列（字段）类型
            2	列（字段）标志
            1	整型值精度
            2	填充值（0x00）
            n	默认值（Length Coded String）
    */
    
    if ((long long) p_st_fragment->u64_parsed_index < p_st_resp->ll_field_count)
    {
        if (bytebuff_skip(p_st_data, bytebuff_remaining(p_st_data))== FR_FAIL)
            return FR_FAIL;
        return FR_SUCC;
    }
    else if ((long long) p_st_fragment->u64_parsed_index > p_st_resp->ll_field_count)
        return FR_FAIL;
    
    if (bytebuff_skip(p_st_data, bytebuff_remaining(p_st_data))== FR_FAIL)
        return FR_FAIL;
    
    p_st_resp->i_status = MYSQL_RESPONSE_RESULT_EOF;
    return FR_SUCC;
}

FR_STATIC int __dissect_result_row(L7session_mysql_private *p_st_private)
{
    L7_fragment_mysql *p_st_fragment = p_st_private->p_st_fragment;
    Frbyte_Packet *p_st_data = p_st_fragment->p_st_data;
    L7_resp_mysql *p_st_resp = p_st_private->p_st_resp;
    
    //todo不做解析
    
    /*
        1	结构头（0x00）
        (列数量 + 7 + 2) / 8	空位图
        n	字段值
        ...	（一行数据中包含多个字段值）
    */
        
    p_st_resp->arr_effect_rows[p_st_resp->ui_response_count++] = 0;
    
    if (bytebuff_skip(p_st_data, bytebuff_remaining(p_st_data))== FR_FAIL)
        return FR_FAIL;
    
    return FR_SUCC;
}

int mysql_check_response(L7session_mysql_private *p_st_private, int i_result)
{
    L7_resp_mysql *p_st_resp = NULL;
    
    if (p_st_private == NULL)
        return MYSQL_RESPONSE_ERR;
    
    p_st_resp = p_st_private->p_st_resp;
    if (p_st_resp == NULL)
        return MYSQL_RESPONSE_RECEIVE;
    
    switch (i_result)
    {
        case 0x00:
        case 0xFF:
        case 0xFE:
            return MYSQL_RESPONSE_RECEIVE;
        default:
            switch (p_st_resp->i_status)
            {
                case MYSQL_RESPONSE_NORMAL:
                    return MYSQL_RESPONSE_RECEIVE;
                case MYSQL_RESPONSE_RESULT_FIELD:
                case MYSQL_RESPONSE_RESULT_ROW:
                    return MYSQL_RESPONSE_SKIP;
                default:
                    return MYSQL_RESPONSE_ERR;
            }
            break;
    }
    return MYSQL_RESPONSE_ERR;
}

/*******************************************************************************
*  FUNC     :  response信息解析
*  ARGS     :  p_st_private
*  RTN      :  FR_SUCC (true)
*              FR_FAIL (false)
*  NOTE     :
*******************************************************************************/
int mysql_dissect_response(Conntrack *p_st_conn
                        , L7session_mysql_private *p_st_private
                        , Frhash_header *p_st_hash_report
                        , struct timeval *p_st_timestamp)
{
    int i_ret = FR_SUCC;
    Sql_resultset *p_st_resultset = NULL;
    Sql_statement *p_st_statement = NULL;
    L7_fragment_mysql *p_st_fragment = NULL;
    L7_req_mysql *p_st_req = NULL;
    L7_resp_mysql *p_st_resp = NULL;
    Frbyte_Packet *p_bytebuff_packet = NULL;
    int i_result = 0;
    int i_whole_flg = 0;
    long long ll_effect_row = 0;
    
    /*
        第一个字节为以下类型报文：
            OK 响应报文	    0x00
            Error 响应报文	0xFF
            Result Set 报文	0x01 - 0xFA
            Field 报文	    0x01 - 0xFA
            Row Data 报文	0x01 - 0xFA
            EOF 报文	    0xFE
    */
    if (p_st_private == NULL || p_st_private->p_st_fragment == NULL || p_st_private->p_st_sess == NULL)
        return APM_L7_ANALYZE_FAIL;
    
    /* 之前没有接到request请求，则忽视该响应 */
    if (p_st_private->p_st_req == NULL)
    {
        frdbg("There no request before response!\n");
        return APM_L7_ANALYZE_SUCC;
    }
    
    p_st_fragment = p_st_private->p_st_fragment;
    p_bytebuff_packet = p_st_fragment->p_st_data;
    p_st_req = p_st_private->p_st_req;
    
    /* 创建response */
    if (p_st_private->p_st_resp == NULL)
    {
        p_st_private->p_st_resp = (L7_resp_mysql *)frmalloc(sizeof(L7_resp_mysql));
        if (p_st_private->p_st_resp == NULL)
            goto err;
        
        /* 初始化response */
        memset(p_st_private->p_st_resp, 0x0, sizeof(L7_resp_mysql));
    }
    p_st_resp = p_st_private->p_st_resp;
    /* 取得statement和resultset索引 */
    p_st_statement = p_st_req->p_st_statement;
    p_st_resultset = p_st_req->p_st_resultset;
    
    if (bytebuff_peek_int(p_bytebuff_packet, 1, (uint32_t *)&i_result, BYTE_LITTLE_ENDIAN) == FR_FAIL)
        goto err;
    
    switch (i_result)
    {
        case 0x00:
        {
            //先判断是否为login的数据包
            if (p_st_req->u8_request_type == MYSQL_REQUEST_LONGIN)
            {
//                Frreport_body_data *p_st_report_body = NULL;
//                Frreport_data *p_st_report_data = NULL;
                
                p_st_req->u8_request_type = MYSQL_REQUEST_START;
                p_st_private->p_st_sess->i_login = MYSQL_LOGIN_SUCC;
                /* 销毁response数据 */
                /* 销毁request数据 */
                mysql_destroy_request(&p_st_private->p_st_req);
                mysql_destroy_response(&p_st_private->p_st_resp);
                
                //login成功。设置login时间
                p_st_private->p_st_sess->st_login.tv_sec = p_st_timestamp->tv_sec;
                p_st_private->p_st_sess->st_login.tv_usec = p_st_timestamp->tv_usec;
                
//                p_st_report_body = mysql_new_report_body(p_st_conn, p_st_private->p_st_sess);
//                /* 创建report body失败时，不释放做成的request */
//                if (p_st_report_body == NULL)
//                {
//                    frdbg("mysql_new_report_body failed!\n");
//                    return APM_L7_ANALYZE_SUCC;
//                }
//                p_st_report_data = frreport_data_new();
//                if (p_st_report_data == NULL)
//                {
//                    frdbg("frreport_data_new failed!\n");
//                    return FR_FAIL;
//                }
//                i_ret = frreport_data_add_body(p_st_report_data, &p_st_report_body);
//                if (i_ret != FR_SUCC)
//                {
//                    frreport_data_free(&p_st_report_data);
//                    return FR_FAIL;
//                }
//                
//                i_ret = frreport_data_hash(p_st_hash_report, p_st_report_data);
//                if (i_ret != FR_SUCC)
//                {
//                    frreport_data_free(&p_st_report_data);
//                    frreport_body_l7list_free(pp_st_body_req);
//                    return FR_FAIL;
//                }
                
                return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);
            }
            i_whole_flg = MYSQL_TRUE;
            bytebuff_skip(p_bytebuff_packet, 1);
            i_ret = __dissect_ok_result(p_st_private, p_st_fragment->u64_total_len, &ll_effect_row);
            if (i_ret != FR_SUCC)
                goto err;
            
            p_st_resp->ui_response_count = 1;
            p_st_resp->arr_effect_rows[0] = ll_effect_row;
            break;
        }
        case 0xFF:
        {
            //先判断是否为login的数据包
            if (p_st_req->u8_request_type == MYSQL_REQUEST_LONGIN)
            {
                p_st_req->u8_request_type = MYSQL_REQUEST_START;
                /* 销毁response数据 */
                /* 销毁request数据 */
                mysql_destroy_request(&p_st_private->p_st_req);
                mysql_destroy_response(&p_st_private->p_st_resp);
                //失败了不上报
                
                return APM_L7_ANALYZE_SET_RESP(APM_L7_ANALYZE_SUCC);
            }
            i_whole_flg = MYSQL_TRUE;
            bytebuff_skip(p_bytebuff_packet, 1);
            i_ret = __dissect_err_result(p_st_private);
            if (i_ret != FR_SUCC)
                goto err;
            
            p_st_resp->ui_response_count = 1;
            p_st_resp->arr_effect_rows[0] = 0;
            break;
        }
        case 0xFE:
        {
            /*
                4.0 及之前的版本
                    1	EOF值（0xFE）
                4.1 及之后的版本
                    1	EOF值（0xFE）
                    2	告警计数
                    2	状态标志位
            */
            bytebuff_skip(p_bytebuff_packet, 1);
            if (p_st_resp->i_status == MYSQL_RESPONSE_RESULT_EOF)
            {
                p_st_resp->i_status = MYSQL_RESPONSE_RESULT_ROW;
                i_whole_flg = MYSQL_FALSE;
            }
            else if (p_st_resp->i_status == MYSQL_RESPONSE_RESULT_ROW)
            {
                i_whole_flg = MYSQL_TRUE;
                p_st_resp->i_status = MYSQL_RESPONSE_RESULT_ROW_EOF;
            }
            else
                goto err;
            
            if (p_st_private->p_st_sess->i_version == MYSQL_VERSION_OLD)
            {
                //错误的版本？
                if (bytebuff_remaining(p_bytebuff_packet) > 0)
                    goto err;
            }
            else
            {
                if (bytebuff_skip(p_bytebuff_packet, bytebuff_remaining(p_bytebuff_packet))== FR_FAIL)
                    goto err;
            }
            break;
        }
        default:
        {
            i_whole_flg = MYSQL_FALSE;
            
            switch (p_st_resp->i_status)
            {
                case MYSQL_RESPONSE_NORMAL:
                {
                    i_ret = __dissect_result_header(p_st_private);
                    if (i_ret != FR_SUCC)
                        goto err;
                    break;
                }
                case MYSQL_RESPONSE_RESULT_FIELD:
                {
                    bytebuff_skip(p_bytebuff_packet, 1);
                    i_ret = __dissect_result_field(p_st_private);
                    if (i_ret != FR_SUCC)
                        goto err;
                    break;
                }
                case MYSQL_RESPONSE_RESULT_ROW:
                {
                    bytebuff_skip(p_bytebuff_packet, 1);
                    i_ret = __dissect_result_row(p_st_private);
                    if (i_ret != FR_SUCC)
                        goto err;
                    break;
                }
                default:
                    goto err;
            }
        }
    }
    if (i_whole_flg != MYSQL_TRUE)
        return APM_L7_ANALYZE_TODO;
    
    //若为真正的response，说明已经确定方向
    if( frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
        p_st_conn->flg_direct_known = FR_DIRECT_KNOWN;
    
    i_ret = __mysql_report_data(p_st_conn, p_st_private, p_st_hash_report, p_st_timestamp);
    
    return i_ret;

err:
    mysql_destroy_request(&p_st_private->p_st_req);
    if (p_st_private->p_st_resp)
        mysql_destroy_response(&p_st_private->p_st_resp);
    return APM_L7_ANALYZE_FAIL;
}

